Load libraries

This project uses renv to keep track of installed packages. Install renv if not installed and load dependencies with renv::restore().

install.packages("renv")
renv::restore()
library(readr)
library(dplyr)
library(tidyr)
library(reshape2)
library(GenomicRanges)
library(pheatmap)
library(tibble)
library(ggplot2)
library(stringr)
library(cowplot)
library(markdown)
library(RColorBrewer)
library(GenomicAlignments)
library(reshape2)

Read data

  1. Get list of samples
samples <- read_tsv("config/samples.tsv", show_col_types = FALSE)
units <- read_tsv("config/units.tsv", show_col_types = FALSE)
sample_units <- dplyr::left_join(samples, units, by = "sample_name") %>%
  unite(sample_unit, sample_name, unit_name, remove = FALSE)
sample_units
  1. Read Samtools idxstats to get human coverage for normalization

Notes:

idxstats_exogenousrna_dir <-
  "results/samtools_idxstats/exogenous_rna/"

idxstats_human_dir <-
  "results/samtools_idxstats/Homo_sapiens.GRCh38.dna.primary_assembly/"

bowtie2_human_logs <-
  "results/logs/bowtie2/Homo_sapiens.GRCh38.dna.primary_assembly/"

idxstats <- tibble()

for (row in seq_len(nrow(sample_units))) {
  sample <- sample_units[row, ]$sample_unit

  # Read `idsxstats` for exogenous mapped reads
  exogenous_rna_stats <- read_tsv(
    file.path(idxstats_exogenousrna_dir, sprintf("%s.bam.idxstats", sample)),
    col_names = c(
      "sequence_name", "sequence_length",
      "mapped_reads", "unmapped_reads"
    ),
    col_types = "ciii"
  )
  exogenous_rna_mapped_reads <- exogenous_rna_stats %>%
    filter(!sequence_name %in% c("*")) %>%
    select(sequence_name, mapped_reads) %>%
    mutate(sample = sample)

  # Read `idxstats` for human mapped reads
  human_stats <- read_tsv(
    file.path(idxstats_human_dir, sprintf("%s.bam.idxstats", sample)),
    col_names = c(
      "sequence_name", "sequence_length",
      "mapped_reads", "unmapped_reads"
    ),
    col_types = "ciii"
  )
  grch38_mapped_reads <- human_stats %>%
    filter(!sequence_name %in% c("*")) %>%
    select(mapped_reads) %>%
    sum()
  grch38_mapped_reads <- tibble(
    sequence_name = "grch38_mapped_reads",
    mapped_reads = grch38_mapped_reads,
    sample = sample
  )

  # Read bowtie2 logs for unmapped reads
  bowtie2_log <- readLines(
    file.path(bowtie2_human_logs, sprintf("%s.log", sample))
  )
  total_pairs <- strtoi(str_split(bowtie2_log[1], " ")[[1]][1])
  total_reads <- total_pairs * 2
  unmapped_reads <- tibble(
    sequence_name = "unmapped",
    mapped_reads = total_reads - grch38_mapped_reads$mapped_reads,
    sample = sample
  )

  # Consolidate counts for rows
  idxstats <- rbind(
    idxstats,
    exogenous_rna_mapped_reads,
    grch38_mapped_reads,
    unmapped_reads
  )
}
idxstats
  1. Read bedpe files to get exogenous rna coverage of paired reads
bedpe_data <- tibble()
for (sample in sample_units$sample_unit) {
  data <-
    readr::read_tsv(
      sprintf(
        "results/alignments/exogenous_rna/bedpe/%s.bedpe", sample
      ),
      col_names = c(
        "chrom1", "chrom1Start", "chrom1End",
        "chrom2", "chrom2Start", "chrom2End",
        "name", "score", "strand1", "strand2"
      ),
      col_types = "ciiciicicc"
    )
  bedpe_data <- tibble(rbind(
    bedpe_data,
    cbind(
      sample = sample,
      data
    )
  ))
}
bedpe_data

Coverage

Concordant vs Discordant paired reads

Concordant pairs are pairs of reads that:

  • Align on the same pegRNA
  • Align within 500 bp of each other
  • Align in the expected forward-reverse orientation (--> .. <--)

Discordant reads aligned but whose mate:

  • Did not align (on the pegRNA)
  • Aligned more than 500 bp away
  • Aligned in an unexpected orientation
## Config and function definition

bam_dir <- "results/alignments/exogenous_rna/sorted"

last_day <- 0
cols <- brewer.pal(n = 5, name = "RdBu")

concordant_cell_line_colors <- list(
  "Parental" = "#CA0020",
  "P1E10" = "#0571B0"
)

discordant_cell_line_colors <- list(
  "Parental" = "#F4A582",
  "P1E10" = "#92C5DE"
)

# Exogenous RNA mixtures
rna_mixes <- tibble()
for (mix in c("mastermix1", "mastermix2")) {
  t <- readDNAStringSet(sprintf("data/references/%s.fa", mix))
  rna_mixes <- rbind(rna_mixes, tibble(
    exogenous_rna = mix,
    rna_species = word(t@ranges@NAMES, 1),
    length = t@ranges@width
  ))
}

get_pegrna_plot_data <- function(sequence_name,
                                 normalization_factor,
                                 mix = NA) {
  samples <- sample_units %>%
    full_join(rna_mixes, by = "exogenous_rna") %>%
    arrange(.data$exogenous_rna, day, cell_line) %>%
    filter(rna_species == sequence_name)

  if (!is.na(mix)) {
    samples <- samples %>% filter(.data$exogenous_rna == mix)
  }
  samples <- samples %>% pull(.data$sample_unit)

  plot_data <- list()

  for (s in samples) {
    if (normalization_factor == "exogenous_rna_mapped_reads") {
      norm_seqname <- sequence_name
    } else if (normalization_factor == "grch38_mapped_reads") {
      norm_seqname <- "grch38_mapped_reads"
    }
    normalization_read_count <- idxstats %>%
      filter(sample == s) %>%
      filter(sequence_name == norm_seqname) %>%
      pull(.data$mapped_reads)

    cell_line <- sample_units %>%
      filter(.data$sample_unit == s) %>%
      pull(cell_line)

    day <- sample_units %>%
      filter(.data$sample_unit == s) %>%
      pull(day)

    which <- GRanges(sprintf("%s:1-%i", sequence_name, rna_mixes %>%
      filter(sequence_name == sequence_name) %>%
      pull(length)))
    param_discordant <- ScanBamParam(
      flag = scanBamFlag(isProperPair = FALSE),
      which = which
    )

    discordant <- readGAlignments(sprintf("%s/%s.bam", bam_dir, s),
      param = param_discordant
    )
    cov_discordant <- coverage(discordant)
    cov_discordant_norm <- cov_discordant / normalization_read_count

    which <- GRanges(sprintf("%s:1-%i", sequence_name, rna_mixes %>%
      filter(sequence_name == sequence_name) %>%
      pull(length)))
    param_concordant <- ScanBamParam( # nolint
      flag = scanBamFlag(isProperPair = TRUE),
      which = which
    )

    df_complete <- bedpe_data %>%
      filter(sample == s) %>%
      filter(.data$chrom1 == sequence_name)
    concordant <- GRanges(
      ranges = IRanges(
        start = df_complete$chrom1Start,
        end = df_complete$chrom2End
      ),
      seqnames = df_complete$chrom1
    )
    cov_concordant <- coverage(concordant)
    cov_concordant_norm <- cov_concordant / normalization_read_count

    plot_data[[s]] <- list(
      cell_line = cell_line,
      day = day,
      discordant = cov_discordant_norm,
      concordant = cov_concordant_norm
    )
  }
  return(plot_data)
}

pegrna_plots <- function(sequence_name,
                         normalization_factor,
                         mix = NA,
                         ylim = NA,
                         ylab,
                         vlines = NA) {
  plot_data <- get_pegrna_plot_data(
    sequence_name = sequence_name,
    normalization_factor = normalization_factor,
    mix = mix
  )

  # Find largest value for rna_species
  if (is.na(ylim)) {
    m <- 0
    for (s in names(plot_data)) {
      m <- max(
        m,
        max(plot_data[[s]][["concordant"]][[sequence_name]]),
        max(plot_data[[s]][["discordant"]][[sequence_name]])
      )
    }
    ylim <- c(0, m)
  }

  last_day <- 0

  par(mfrow = c(2, 1))

  for (s in names(plot_data)) {
    series_data_concordant <- plot_data[[s]][["concordant"]][[sequence_name]]
    series_data_discordant <- plot_data[[s]][["discordant"]][[sequence_name]]

    day <- plot_data[[s]][["day"]]
    cell_line <- plot_data[[s]][["cell_line"]]
    concordant_color <- concordant_cell_line_colors[[cell_line]]
    discordant_color <- discordant_cell_line_colors[[cell_line]]

    if (day != last_day) {
      plot(series_data_concordant,
        type = "l",
        col = concordant_color,
        ylim = ylim,
        main = sprintf("Day %s", day),
        xlab = sprintf("%s position", sequence_name),
        ylab = ylab
      )
      lines(series_data_discordant,
        type = "l",
        col = discordant_cell_line_colors[[plot_data[[s]][["cell_line"]]]]
      )
    } else {
      lines(series_data_concordant,
        type = "l",
        col = concordant_color
      )
      lines(series_data_discordant,
        type = "l",
        col = discordant_color
      )
    }
    legend("top",
      legend = c(
        "Parental:Concordant",
        "P1E10:Concordant",
        "Parental:Discordant",
        "P1E10:Discordant"
      ),
      col = unlist(c(
        concordant_cell_line_colors,
        discordant_cell_line_colors
      )),
      lty = 1,
      cex = 0.75
    )
    for (vline in vlines) {
      abline(v = vline, lty = 2)
      text(
        x = vline, y = 0, as.character(vline),
        pos = 2, cex = 0.75, lwd = 0.5
      )
    }
    last_day <- day
  }
}

VEGFA

rna_mix_rows <- rna_mixes %>% filter(grepl("VEGFA", rna_species))

for (norm_factor in c("exogenous_rna_mapped_reads", "grch38_mapped_reads")) {
  for (row in seq_len(nrow(rna_mix_rows))) {
    rna_species <- rna_mix_rows[[row, "rna_species"]]
    mix <- rna_mix_rows[[row, "exogenous_rna"]]

    pegrna_plots(
      sequence_name = rna_species,
      normalization_factor = norm_factor,
      mix = mix,
      ylab = sprintf("%s coverage (normalized to %s)", mix, norm_factor),
    )
  }
}

FANCF

for (norm_factor in c("exogenous_rna_mapped_reads", "grch38_mapped_reads")) {
  for (rna_species in rna_mixes %>%
    filter(grepl("FANCF", rna_species)) %>%
    pull(rna_species)) {
    pegrna_plots(
      sequence_name = rna_species,
      normalization_factor = norm_factor,
      ylab = sprintf("Coverage (normalized to %s)", norm_factor),
    )
  }
}

HEK3

for (norm_factor in c("exogenous_rna_mapped_reads", "grch38_mapped_reads")) {
  for (rna_species in rna_mixes %>%
    filter(grepl("HEK3", rna_species)) %>%
    pull(rna_species)) {
    pegrna_plots(
      sequence_name = rna_species,
      normalization_factor = norm_factor,
      ylab = sprintf("Coverage (normalized to %s)", norm_factor),
    )
  }
}

DNMT1

for (norm_factor in c("exogenous_rna_mapped_reads", "grch38_mapped_reads")) {
  for (rna_species in rna_mixes %>%
    filter(grepl("DNMT1", rna_species)) %>%
    pull(rna_species)) {
    pegrna_plots(
      sequence_name = rna_species,
      normalization_factor = norm_factor,
      ylab = sprintf("Coverage (normalized to %s)", norm_factor),
    )
  }
}

RUNX1

for (norm_factor in c("exogenous_rna_mapped_reads", "grch38_mapped_reads")) {
  for (rna_species in rna_mixes %>%
    filter(grepl("RUNX1", rna_species)) %>%
    pull(rna_species)) {
    pegrna_plots(
      sequence_name = rna_species,
      normalization_factor = norm_factor,
      ylab = sprintf("Coverage (normalized to %s)", norm_factor),
    )
  }
}

EMX1

for (norm_factor in c("exogenous_rna_mapped_reads", "grch38_mapped_reads")) {
  for (rna_species in rna_mixes %>%
    filter(grepl("EMX1", rna_species)) %>%
    pull(rna_species)) {
    pegrna_plots(
      sequence_name = rna_species,
      normalization_factor = norm_factor,
      ylab = sprintf("Coverage (normalized to %s)", norm_factor),
    )
  }
}

RNF2

for (norm_factor in c("exogenous_rna_mapped_reads", "grch38_mapped_reads")) {
  for (rna_species in rna_mixes %>%
    filter(grepl("RNF2", rna_species)) %>%
    pull(rna_species)) {
    pegrna_plots(
      sequence_name = rna_species,
      normalization_factor = norm_factor,
      ylab = sprintf("Coverage (normalized to %s)", norm_factor),
    )
  }
}

LS0tCnRpdGxlOiAiQWRhbXNvbiBzbWFsbFJOQSAtIHBlZ1JOQSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIGNvZGVfZm9sZGluZzogaGlkZQotLS0KCiMjIExvYWQgbGlicmFyaWVzCgpUaGlzIHByb2plY3QgdXNlcyBbYHJlbnZgXShodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL3JlbnYvYXJ0aWNsZXMvcmVudi5odG1sKQp0byBrZWVwIHRyYWNrIG9mIGluc3RhbGxlZCBwYWNrYWdlcy4gSW5zdGFsbCBgcmVudmAgaWYgbm90IGluc3RhbGxlZCBhbmQgbG9hZApkZXBlbmRlbmNpZXMgd2l0aCBgcmVudjo6cmVzdG9yZSgpYC4KCmBgYHIKaW5zdGFsbC5wYWNrYWdlcygicmVudiIpCnJlbnY6OnJlc3RvcmUoKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoR2Vub21pY1JhbmdlcykKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeSh0aWJibGUpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkobWFya2Rvd24pCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KEdlbm9taWNBbGlnbm1lbnRzKQpsaWJyYXJ5KHJlc2hhcGUyKQpgYGAKCiMjIFJlYWQgZGF0YQoKMS4gR2V0IGxpc3Qgb2Ygc2FtcGxlcwoKYGBge3J9CnNhbXBsZXMgPC0gcmVhZF90c3YoImNvbmZpZy9zYW1wbGVzLnRzdiIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpCnVuaXRzIDwtIHJlYWRfdHN2KCJjb25maWcvdW5pdHMudHN2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKc2FtcGxlX3VuaXRzIDwtIGRwbHlyOjpsZWZ0X2pvaW4oc2FtcGxlcywgdW5pdHMsIGJ5ID0gInNhbXBsZV9uYW1lIikgJT4lCiAgdW5pdGUoc2FtcGxlX3VuaXQsIHNhbXBsZV9uYW1lLCB1bml0X25hbWUsIHJlbW92ZSA9IEZBTFNFKQpzYW1wbGVfdW5pdHMKYGBgCgoyLiBSZWFkIFNhbXRvb2xzIGBpZHhzdGF0c2AgdG8gZ2V0IGh1bWFuIGNvdmVyYWdlIGZvciBub3JtYWxpemF0aW9uCgpOb3RlczoKCiogVGhlIGNvdW50cyBpbmNsdWRlIHRoZSB0b3RhbCBudW1iZXIgb2YgcmVhZHMgYWxpZ25lZCwgdGhleSAKICBhcmUgbm90IGxpbWl0ZWQgdG8gdW5pcXVlbHkgYWxpZ25lZCByZWFkcy4KKiBUaGUgY291bnRzIGFyZSByZWFkcywgbm90IHBhaXJzIG9yIGZyYWdtZW50cwoKYGBge3J9CmlkeHN0YXRzX2V4b2dlbm91c3JuYV9kaXIgPC0KICAicmVzdWx0cy9zYW10b29sc19pZHhzdGF0cy9leG9nZW5vdXNfcm5hLyIKCmlkeHN0YXRzX2h1bWFuX2RpciA8LQogICJyZXN1bHRzL3NhbXRvb2xzX2lkeHN0YXRzL0hvbW9fc2FwaWVucy5HUkNoMzguZG5hLnByaW1hcnlfYXNzZW1ibHkvIgoKYm93dGllMl9odW1hbl9sb2dzIDwtCiAgInJlc3VsdHMvbG9ncy9ib3d0aWUyL0hvbW9fc2FwaWVucy5HUkNoMzguZG5hLnByaW1hcnlfYXNzZW1ibHkvIgoKaWR4c3RhdHMgPC0gdGliYmxlKCkKCmZvciAocm93IGluIHNlcV9sZW4obnJvdyhzYW1wbGVfdW5pdHMpKSkgewogIHNhbXBsZSA8LSBzYW1wbGVfdW5pdHNbcm93LCBdJHNhbXBsZV91bml0CgogICMgUmVhZCBgaWRzeHN0YXRzYCBmb3IgZXhvZ2Vub3VzIG1hcHBlZCByZWFkcwogIGV4b2dlbm91c19ybmFfc3RhdHMgPC0gcmVhZF90c3YoCiAgICBmaWxlLnBhdGgoaWR4c3RhdHNfZXhvZ2Vub3Vzcm5hX2Rpciwgc3ByaW50ZigiJXMuYmFtLmlkeHN0YXRzIiwgc2FtcGxlKSksCiAgICBjb2xfbmFtZXMgPSBjKAogICAgICAic2VxdWVuY2VfbmFtZSIsICJzZXF1ZW5jZV9sZW5ndGgiLAogICAgICAibWFwcGVkX3JlYWRzIiwgInVubWFwcGVkX3JlYWRzIgogICAgKSwKICAgIGNvbF90eXBlcyA9ICJjaWlpIgogICkKICBleG9nZW5vdXNfcm5hX21hcHBlZF9yZWFkcyA8LSBleG9nZW5vdXNfcm5hX3N0YXRzICU+JQogICAgZmlsdGVyKCFzZXF1ZW5jZV9uYW1lICVpbiUgYygiKiIpKSAlPiUKICAgIHNlbGVjdChzZXF1ZW5jZV9uYW1lLCBtYXBwZWRfcmVhZHMpICU+JQogICAgbXV0YXRlKHNhbXBsZSA9IHNhbXBsZSkKCiAgIyBSZWFkIGBpZHhzdGF0c2AgZm9yIGh1bWFuIG1hcHBlZCByZWFkcwogIGh1bWFuX3N0YXRzIDwtIHJlYWRfdHN2KAogICAgZmlsZS5wYXRoKGlkeHN0YXRzX2h1bWFuX2Rpciwgc3ByaW50ZigiJXMuYmFtLmlkeHN0YXRzIiwgc2FtcGxlKSksCiAgICBjb2xfbmFtZXMgPSBjKAogICAgICAic2VxdWVuY2VfbmFtZSIsICJzZXF1ZW5jZV9sZW5ndGgiLAogICAgICAibWFwcGVkX3JlYWRzIiwgInVubWFwcGVkX3JlYWRzIgogICAgKSwKICAgIGNvbF90eXBlcyA9ICJjaWlpIgogICkKICBncmNoMzhfbWFwcGVkX3JlYWRzIDwtIGh1bWFuX3N0YXRzICU+JQogICAgZmlsdGVyKCFzZXF1ZW5jZV9uYW1lICVpbiUgYygiKiIpKSAlPiUKICAgIHNlbGVjdChtYXBwZWRfcmVhZHMpICU+JQogICAgc3VtKCkKICBncmNoMzhfbWFwcGVkX3JlYWRzIDwtIHRpYmJsZSgKICAgIHNlcXVlbmNlX25hbWUgPSAiZ3JjaDM4X21hcHBlZF9yZWFkcyIsCiAgICBtYXBwZWRfcmVhZHMgPSBncmNoMzhfbWFwcGVkX3JlYWRzLAogICAgc2FtcGxlID0gc2FtcGxlCiAgKQoKICAjIFJlYWQgYm93dGllMiBsb2dzIGZvciB1bm1hcHBlZCByZWFkcwogIGJvd3RpZTJfbG9nIDwtIHJlYWRMaW5lcygKICAgIGZpbGUucGF0aChib3d0aWUyX2h1bWFuX2xvZ3MsIHNwcmludGYoIiVzLmxvZyIsIHNhbXBsZSkpCiAgKQogIHRvdGFsX3BhaXJzIDwtIHN0cnRvaShzdHJfc3BsaXQoYm93dGllMl9sb2dbMV0sICIgIilbWzFdXVsxXSkKICB0b3RhbF9yZWFkcyA8LSB0b3RhbF9wYWlycyAqIDIKICB1bm1hcHBlZF9yZWFkcyA8LSB0aWJibGUoCiAgICBzZXF1ZW5jZV9uYW1lID0gInVubWFwcGVkIiwKICAgIG1hcHBlZF9yZWFkcyA9IHRvdGFsX3JlYWRzIC0gZ3JjaDM4X21hcHBlZF9yZWFkcyRtYXBwZWRfcmVhZHMsCiAgICBzYW1wbGUgPSBzYW1wbGUKICApCgogICMgQ29uc29saWRhdGUgY291bnRzIGZvciByb3dzCiAgaWR4c3RhdHMgPC0gcmJpbmQoCiAgICBpZHhzdGF0cywKICAgIGV4b2dlbm91c19ybmFfbWFwcGVkX3JlYWRzLAogICAgZ3JjaDM4X21hcHBlZF9yZWFkcywKICAgIHVubWFwcGVkX3JlYWRzCiAgKQp9CmlkeHN0YXRzCmBgYAoKMy4gUmVhZCBgYmVkcGVgIGZpbGVzIHRvIGdldCBleG9nZW5vdXMgcm5hIGNvdmVyYWdlIG9mIHBhaXJlZCByZWFkcwoKYGBge3J9CmJlZHBlX2RhdGEgPC0gdGliYmxlKCkKZm9yIChzYW1wbGUgaW4gc2FtcGxlX3VuaXRzJHNhbXBsZV91bml0KSB7CiAgZGF0YSA8LQogICAgcmVhZHI6OnJlYWRfdHN2KAogICAgICBzcHJpbnRmKAogICAgICAgICJyZXN1bHRzL2FsaWdubWVudHMvZXhvZ2Vub3VzX3JuYS9iZWRwZS8lcy5iZWRwZSIsIHNhbXBsZQogICAgICApLAogICAgICBjb2xfbmFtZXMgPSBjKAogICAgICAgICJjaHJvbTEiLCAiY2hyb20xU3RhcnQiLCAiY2hyb20xRW5kIiwKICAgICAgICAiY2hyb20yIiwgImNocm9tMlN0YXJ0IiwgImNocm9tMkVuZCIsCiAgICAgICAgIm5hbWUiLCAic2NvcmUiLCAic3RyYW5kMSIsICJzdHJhbmQyIgogICAgICApLAogICAgICBjb2xfdHlwZXMgPSAiY2lpY2lpY2ljYyIKICAgICkKICBiZWRwZV9kYXRhIDwtIHRpYmJsZShyYmluZCgKICAgIGJlZHBlX2RhdGEsCiAgICBjYmluZCgKICAgICAgc2FtcGxlID0gc2FtcGxlLAogICAgICBkYXRhCiAgICApCiAgKSkKfQpiZWRwZV9kYXRhCmBgYAoKIyMgQ292ZXJhZ2UKCiMjIyBDb25jb3JkYW50IHZzIERpc2NvcmRhbnQgcGFpcmVkIHJlYWRzCgpDb25jb3JkYW50IHBhaXJzIGFyZSBwYWlycyBvZiByZWFkcyB0aGF0OgoKKiBBbGlnbiBvbiB0aGUgc2FtZSBwZWdSTkEKKiBBbGlnbiB3aXRoaW4gNTAwIGJwIG9mIGVhY2ggb3RoZXIKKiBBbGlnbiBpbiB0aGUgZXhwZWN0ZWQgZm9yd2FyZC1yZXZlcnNlIG9yaWVudGF0aW9uIChgLS0+IC4uIDwtLWApCgpEaXNjb3JkYW50IHJlYWRzIGFsaWduZWQgYnV0IHdob3NlIG1hdGU6CgoqIERpZCBub3QgYWxpZ24gKG9uIHRoZSBwZWdSTkEpCiogQWxpZ25lZCBtb3JlIHRoYW4gNTAwIGJwIGF3YXkKKiBBbGlnbmVkIGluIGFuIHVuZXhwZWN0ZWQgb3JpZW50YXRpb24KCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KIyMgQ29uZmlnIGFuZCBmdW5jdGlvbiBkZWZpbml0aW9uCgpiYW1fZGlyIDwtICJyZXN1bHRzL2FsaWdubWVudHMvZXhvZ2Vub3VzX3JuYS9zb3J0ZWQiCgpsYXN0X2RheSA8LSAwCmNvbHMgPC0gYnJld2VyLnBhbChuID0gNSwgbmFtZSA9ICJSZEJ1IikKCmNvbmNvcmRhbnRfY2VsbF9saW5lX2NvbG9ycyA8LSBsaXN0KAogICJQYXJlbnRhbCIgPSAiI0NBMDAyMCIsCiAgIlAxRTEwIiA9ICIjMDU3MUIwIgopCgpkaXNjb3JkYW50X2NlbGxfbGluZV9jb2xvcnMgPC0gbGlzdCgKICAiUGFyZW50YWwiID0gIiNGNEE1ODIiLAogICJQMUUxMCIgPSAiIzkyQzVERSIKKQoKIyBFeG9nZW5vdXMgUk5BIG1peHR1cmVzCnJuYV9taXhlcyA8LSB0aWJibGUoKQpmb3IgKG1peCBpbiBjKCJtYXN0ZXJtaXgxIiwgIm1hc3Rlcm1peDIiKSkgewogIHQgPC0gcmVhZEROQVN0cmluZ1NldChzcHJpbnRmKCJkYXRhL3JlZmVyZW5jZXMvJXMuZmEiLCBtaXgpKQogIHJuYV9taXhlcyA8LSByYmluZChybmFfbWl4ZXMsIHRpYmJsZSgKICAgIGV4b2dlbm91c19ybmEgPSBtaXgsCiAgICBybmFfc3BlY2llcyA9IHdvcmQodEByYW5nZXNATkFNRVMsIDEpLAogICAgbGVuZ3RoID0gdEByYW5nZXNAd2lkdGgKICApKQp9CgpnZXRfcGVncm5hX3Bsb3RfZGF0YSA8LSBmdW5jdGlvbihzZXF1ZW5jZV9uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemF0aW9uX2ZhY3RvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWl4ID0gTkEpIHsKICBzYW1wbGVzIDwtIHNhbXBsZV91bml0cyAlPiUKICAgIGZ1bGxfam9pbihybmFfbWl4ZXMsIGJ5ID0gImV4b2dlbm91c19ybmEiKSAlPiUKICAgIGFycmFuZ2UoLmRhdGEkZXhvZ2Vub3VzX3JuYSwgZGF5LCBjZWxsX2xpbmUpICU+JQogICAgZmlsdGVyKHJuYV9zcGVjaWVzID09IHNlcXVlbmNlX25hbWUpCgogIGlmICghaXMubmEobWl4KSkgewogICAgc2FtcGxlcyA8LSBzYW1wbGVzICU+JSBmaWx0ZXIoLmRhdGEkZXhvZ2Vub3VzX3JuYSA9PSBtaXgpCiAgfQogIHNhbXBsZXMgPC0gc2FtcGxlcyAlPiUgcHVsbCguZGF0YSRzYW1wbGVfdW5pdCkKCiAgcGxvdF9kYXRhIDwtIGxpc3QoKQoKICBmb3IgKHMgaW4gc2FtcGxlcykgewogICAgaWYgKG5vcm1hbGl6YXRpb25fZmFjdG9yID09ICJleG9nZW5vdXNfcm5hX21hcHBlZF9yZWFkcyIpIHsKICAgICAgbm9ybV9zZXFuYW1lIDwtIHNlcXVlbmNlX25hbWUKICAgIH0gZWxzZSBpZiAobm9ybWFsaXphdGlvbl9mYWN0b3IgPT0gImdyY2gzOF9tYXBwZWRfcmVhZHMiKSB7CiAgICAgIG5vcm1fc2VxbmFtZSA8LSAiZ3JjaDM4X21hcHBlZF9yZWFkcyIKICAgIH0KICAgIG5vcm1hbGl6YXRpb25fcmVhZF9jb3VudCA8LSBpZHhzdGF0cyAlPiUKICAgICAgZmlsdGVyKHNhbXBsZSA9PSBzKSAlPiUKICAgICAgZmlsdGVyKHNlcXVlbmNlX25hbWUgPT0gbm9ybV9zZXFuYW1lKSAlPiUKICAgICAgcHVsbCguZGF0YSRtYXBwZWRfcmVhZHMpCgogICAgY2VsbF9saW5lIDwtIHNhbXBsZV91bml0cyAlPiUKICAgICAgZmlsdGVyKC5kYXRhJHNhbXBsZV91bml0ID09IHMpICU+JQogICAgICBwdWxsKGNlbGxfbGluZSkKCiAgICBkYXkgPC0gc2FtcGxlX3VuaXRzICU+JQogICAgICBmaWx0ZXIoLmRhdGEkc2FtcGxlX3VuaXQgPT0gcykgJT4lCiAgICAgIHB1bGwoZGF5KQoKICAgIHdoaWNoIDwtIEdSYW5nZXMoc3ByaW50ZigiJXM6MS0laSIsIHNlcXVlbmNlX25hbWUsIHJuYV9taXhlcyAlPiUKICAgICAgZmlsdGVyKHNlcXVlbmNlX25hbWUgPT0gc2VxdWVuY2VfbmFtZSkgJT4lCiAgICAgIHB1bGwobGVuZ3RoKSkpCiAgICBwYXJhbV9kaXNjb3JkYW50IDwtIFNjYW5CYW1QYXJhbSgKICAgICAgZmxhZyA9IHNjYW5CYW1GbGFnKGlzUHJvcGVyUGFpciA9IEZBTFNFKSwKICAgICAgd2hpY2ggPSB3aGljaAogICAgKQoKICAgIGRpc2NvcmRhbnQgPC0gcmVhZEdBbGlnbm1lbnRzKHNwcmludGYoIiVzLyVzLmJhbSIsIGJhbV9kaXIsIHMpLAogICAgICBwYXJhbSA9IHBhcmFtX2Rpc2NvcmRhbnQKICAgICkKICAgIGNvdl9kaXNjb3JkYW50IDwtIGNvdmVyYWdlKGRpc2NvcmRhbnQpCiAgICBjb3ZfZGlzY29yZGFudF9ub3JtIDwtIGNvdl9kaXNjb3JkYW50IC8gbm9ybWFsaXphdGlvbl9yZWFkX2NvdW50CgogICAgd2hpY2ggPC0gR1JhbmdlcyhzcHJpbnRmKCIlczoxLSVpIiwgc2VxdWVuY2VfbmFtZSwgcm5hX21peGVzICU+JQogICAgICBmaWx0ZXIoc2VxdWVuY2VfbmFtZSA9PSBzZXF1ZW5jZV9uYW1lKSAlPiUKICAgICAgcHVsbChsZW5ndGgpKSkKICAgIHBhcmFtX2NvbmNvcmRhbnQgPC0gU2NhbkJhbVBhcmFtKCAjIG5vbGludAogICAgICBmbGFnID0gc2NhbkJhbUZsYWcoaXNQcm9wZXJQYWlyID0gVFJVRSksCiAgICAgIHdoaWNoID0gd2hpY2gKICAgICkKCiAgICBkZl9jb21wbGV0ZSA8LSBiZWRwZV9kYXRhICU+JQogICAgICBmaWx0ZXIoc2FtcGxlID09IHMpICU+JQogICAgICBmaWx0ZXIoLmRhdGEkY2hyb20xID09IHNlcXVlbmNlX25hbWUpCiAgICBjb25jb3JkYW50IDwtIEdSYW5nZXMoCiAgICAgIHJhbmdlcyA9IElSYW5nZXMoCiAgICAgICAgc3RhcnQgPSBkZl9jb21wbGV0ZSRjaHJvbTFTdGFydCwKICAgICAgICBlbmQgPSBkZl9jb21wbGV0ZSRjaHJvbTJFbmQKICAgICAgKSwKICAgICAgc2VxbmFtZXMgPSBkZl9jb21wbGV0ZSRjaHJvbTEKICAgICkKICAgIGNvdl9jb25jb3JkYW50IDwtIGNvdmVyYWdlKGNvbmNvcmRhbnQpCiAgICBjb3ZfY29uY29yZGFudF9ub3JtIDwtIGNvdl9jb25jb3JkYW50IC8gbm9ybWFsaXphdGlvbl9yZWFkX2NvdW50CgogICAgcGxvdF9kYXRhW1tzXV0gPC0gbGlzdCgKICAgICAgY2VsbF9saW5lID0gY2VsbF9saW5lLAogICAgICBkYXkgPSBkYXksCiAgICAgIGRpc2NvcmRhbnQgPSBjb3ZfZGlzY29yZGFudF9ub3JtLAogICAgICBjb25jb3JkYW50ID0gY292X2NvbmNvcmRhbnRfbm9ybQogICAgKQogIH0KICByZXR1cm4ocGxvdF9kYXRhKQp9CgpwZWdybmFfcGxvdHMgPC0gZnVuY3Rpb24oc2VxdWVuY2VfbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6YXRpb25fZmFjdG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgbWl4ID0gTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICB5bGltID0gTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICB5bGFiLAogICAgICAgICAgICAgICAgICAgICAgICAgdmxpbmVzID0gTkEpIHsKICBwbG90X2RhdGEgPC0gZ2V0X3BlZ3JuYV9wbG90X2RhdGEoCiAgICBzZXF1ZW5jZV9uYW1lID0gc2VxdWVuY2VfbmFtZSwKICAgIG5vcm1hbGl6YXRpb25fZmFjdG9yID0gbm9ybWFsaXphdGlvbl9mYWN0b3IsCiAgICBtaXggPSBtaXgKICApCgogICMgRmluZCBsYXJnZXN0IHZhbHVlIGZvciBybmFfc3BlY2llcwogIGlmIChpcy5uYSh5bGltKSkgewogICAgbSA8LSAwCiAgICBmb3IgKHMgaW4gbmFtZXMocGxvdF9kYXRhKSkgewogICAgICBtIDwtIG1heCgKICAgICAgICBtLAogICAgICAgIG1heChwbG90X2RhdGFbW3NdXVtbImNvbmNvcmRhbnQiXV1bW3NlcXVlbmNlX25hbWVdXSksCiAgICAgICAgbWF4KHBsb3RfZGF0YVtbc11dW1siZGlzY29yZGFudCJdXVtbc2VxdWVuY2VfbmFtZV1dKQogICAgICApCiAgICB9CiAgICB5bGltIDwtIGMoMCwgbSkKICB9CgogIGxhc3RfZGF5IDwtIDAKCiAgcGFyKG1mcm93ID0gYygyLCAxKSkKCiAgZm9yIChzIGluIG5hbWVzKHBsb3RfZGF0YSkpIHsKICAgIHNlcmllc19kYXRhX2NvbmNvcmRhbnQgPC0gcGxvdF9kYXRhW1tzXV1bWyJjb25jb3JkYW50Il1dW1tzZXF1ZW5jZV9uYW1lXV0KICAgIHNlcmllc19kYXRhX2Rpc2NvcmRhbnQgPC0gcGxvdF9kYXRhW1tzXV1bWyJkaXNjb3JkYW50Il1dW1tzZXF1ZW5jZV9uYW1lXV0KCiAgICBkYXkgPC0gcGxvdF9kYXRhW1tzXV1bWyJkYXkiXV0KICAgIGNlbGxfbGluZSA8LSBwbG90X2RhdGFbW3NdXVtbImNlbGxfbGluZSJdXQogICAgY29uY29yZGFudF9jb2xvciA8LSBjb25jb3JkYW50X2NlbGxfbGluZV9jb2xvcnNbW2NlbGxfbGluZV1dCiAgICBkaXNjb3JkYW50X2NvbG9yIDwtIGRpc2NvcmRhbnRfY2VsbF9saW5lX2NvbG9yc1tbY2VsbF9saW5lXV0KCiAgICBpZiAoZGF5ICE9IGxhc3RfZGF5KSB7CiAgICAgIHBsb3Qoc2VyaWVzX2RhdGFfY29uY29yZGFudCwKICAgICAgICB0eXBlID0gImwiLAogICAgICAgIGNvbCA9IGNvbmNvcmRhbnRfY29sb3IsCiAgICAgICAgeWxpbSA9IHlsaW0sCiAgICAgICAgbWFpbiA9IHNwcmludGYoIkRheSAlcyIsIGRheSksCiAgICAgICAgeGxhYiA9IHNwcmludGYoIiVzIHBvc2l0aW9uIiwgc2VxdWVuY2VfbmFtZSksCiAgICAgICAgeWxhYiA9IHlsYWIKICAgICAgKQogICAgICBsaW5lcyhzZXJpZXNfZGF0YV9kaXNjb3JkYW50LAogICAgICAgIHR5cGUgPSAibCIsCiAgICAgICAgY29sID0gZGlzY29yZGFudF9jZWxsX2xpbmVfY29sb3JzW1twbG90X2RhdGFbW3NdXVtbImNlbGxfbGluZSJdXV1dCiAgICAgICkKICAgIH0gZWxzZSB7CiAgICAgIGxpbmVzKHNlcmllc19kYXRhX2NvbmNvcmRhbnQsCiAgICAgICAgdHlwZSA9ICJsIiwKICAgICAgICBjb2wgPSBjb25jb3JkYW50X2NvbG9yCiAgICAgICkKICAgICAgbGluZXMoc2VyaWVzX2RhdGFfZGlzY29yZGFudCwKICAgICAgICB0eXBlID0gImwiLAogICAgICAgIGNvbCA9IGRpc2NvcmRhbnRfY29sb3IKICAgICAgKQogICAgfQogICAgbGVnZW5kKCJ0b3AiLAogICAgICBsZWdlbmQgPSBjKAogICAgICAgICJQYXJlbnRhbDpDb25jb3JkYW50IiwKICAgICAgICAiUDFFMTA6Q29uY29yZGFudCIsCiAgICAgICAgIlBhcmVudGFsOkRpc2NvcmRhbnQiLAogICAgICAgICJQMUUxMDpEaXNjb3JkYW50IgogICAgICApLAogICAgICBjb2wgPSB1bmxpc3QoYygKICAgICAgICBjb25jb3JkYW50X2NlbGxfbGluZV9jb2xvcnMsCiAgICAgICAgZGlzY29yZGFudF9jZWxsX2xpbmVfY29sb3JzCiAgICAgICkpLAogICAgICBsdHkgPSAxLAogICAgICBjZXggPSAwLjc1CiAgICApCiAgICBmb3IgKHZsaW5lIGluIHZsaW5lcykgewogICAgICBhYmxpbmUodiA9IHZsaW5lLCBsdHkgPSAyKQogICAgICB0ZXh0KAogICAgICAgIHggPSB2bGluZSwgeSA9IDAsIGFzLmNoYXJhY3Rlcih2bGluZSksCiAgICAgICAgcG9zID0gMiwgY2V4ID0gMC43NSwgbHdkID0gMC41CiAgICAgICkKICAgIH0KICAgIGxhc3RfZGF5IDwtIGRheQogIH0KfQpgYGAKCiMjIyBWRUdGQQoKYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpybmFfbWl4X3Jvd3MgPC0gcm5hX21peGVzICU+JSBmaWx0ZXIoZ3JlcGwoIlZFR0ZBIiwgcm5hX3NwZWNpZXMpKQoKZm9yIChub3JtX2ZhY3RvciBpbiBjKCJleG9nZW5vdXNfcm5hX21hcHBlZF9yZWFkcyIsICJncmNoMzhfbWFwcGVkX3JlYWRzIikpIHsKICBmb3IgKHJvdyBpbiBzZXFfbGVuKG5yb3cocm5hX21peF9yb3dzKSkpIHsKICAgIHJuYV9zcGVjaWVzIDwtIHJuYV9taXhfcm93c1tbcm93LCAicm5hX3NwZWNpZXMiXV0KICAgIG1peCA8LSBybmFfbWl4X3Jvd3NbW3JvdywgImV4b2dlbm91c19ybmEiXV0KCiAgICBwZWdybmFfcGxvdHMoCiAgICAgIHNlcXVlbmNlX25hbWUgPSBybmFfc3BlY2llcywKICAgICAgbm9ybWFsaXphdGlvbl9mYWN0b3IgPSBub3JtX2ZhY3RvciwKICAgICAgbWl4ID0gbWl4LAogICAgICB5bGFiID0gc3ByaW50ZigiJXMgY292ZXJhZ2UgKG5vcm1hbGl6ZWQgdG8gJXMpIiwgbWl4LCBub3JtX2ZhY3RvciksCiAgICApCiAgfQp9CmBgYAoKIyMjIEZBTkNGCgpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CmZvciAobm9ybV9mYWN0b3IgaW4gYygiZXhvZ2Vub3VzX3JuYV9tYXBwZWRfcmVhZHMiLCAiZ3JjaDM4X21hcHBlZF9yZWFkcyIpKSB7CiAgZm9yIChybmFfc3BlY2llcyBpbiBybmFfbWl4ZXMgJT4lCiAgICBmaWx0ZXIoZ3JlcGwoIkZBTkNGIiwgcm5hX3NwZWNpZXMpKSAlPiUKICAgIHB1bGwocm5hX3NwZWNpZXMpKSB7CiAgICBwZWdybmFfcGxvdHMoCiAgICAgIHNlcXVlbmNlX25hbWUgPSBybmFfc3BlY2llcywKICAgICAgbm9ybWFsaXphdGlvbl9mYWN0b3IgPSBub3JtX2ZhY3RvciwKICAgICAgeWxhYiA9IHNwcmludGYoIkNvdmVyYWdlIChub3JtYWxpemVkIHRvICVzKSIsIG5vcm1fZmFjdG9yKSwKICAgICkKICB9Cn0KYGBgCgojIyMgSEVLMwoKYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpmb3IgKG5vcm1fZmFjdG9yIGluIGMoImV4b2dlbm91c19ybmFfbWFwcGVkX3JlYWRzIiwgImdyY2gzOF9tYXBwZWRfcmVhZHMiKSkgewogIGZvciAocm5hX3NwZWNpZXMgaW4gcm5hX21peGVzICU+JQogICAgZmlsdGVyKGdyZXBsKCJIRUszIiwgcm5hX3NwZWNpZXMpKSAlPiUKICAgIHB1bGwocm5hX3NwZWNpZXMpKSB7CiAgICBwZWdybmFfcGxvdHMoCiAgICAgIHNlcXVlbmNlX25hbWUgPSBybmFfc3BlY2llcywKICAgICAgbm9ybWFsaXphdGlvbl9mYWN0b3IgPSBub3JtX2ZhY3RvciwKICAgICAgeWxhYiA9IHNwcmludGYoIkNvdmVyYWdlIChub3JtYWxpemVkIHRvICVzKSIsIG5vcm1fZmFjdG9yKSwKICAgICkKICB9Cn0KYGBgCgoKCiMjIyMgRE5NVDEKCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KZm9yIChub3JtX2ZhY3RvciBpbiBjKCJleG9nZW5vdXNfcm5hX21hcHBlZF9yZWFkcyIsICJncmNoMzhfbWFwcGVkX3JlYWRzIikpIHsKICBmb3IgKHJuYV9zcGVjaWVzIGluIHJuYV9taXhlcyAlPiUKICAgIGZpbHRlcihncmVwbCgiRE5NVDEiLCBybmFfc3BlY2llcykpICU+JQogICAgcHVsbChybmFfc3BlY2llcykpIHsKICAgIHBlZ3JuYV9wbG90cygKICAgICAgc2VxdWVuY2VfbmFtZSA9IHJuYV9zcGVjaWVzLAogICAgICBub3JtYWxpemF0aW9uX2ZhY3RvciA9IG5vcm1fZmFjdG9yLAogICAgICB5bGFiID0gc3ByaW50ZigiQ292ZXJhZ2UgKG5vcm1hbGl6ZWQgdG8gJXMpIiwgbm9ybV9mYWN0b3IpLAogICAgKQogIH0KfQpgYGAKCgojIyMgUlVOWDEKCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KZm9yIChub3JtX2ZhY3RvciBpbiBjKCJleG9nZW5vdXNfcm5hX21hcHBlZF9yZWFkcyIsICJncmNoMzhfbWFwcGVkX3JlYWRzIikpIHsKICBmb3IgKHJuYV9zcGVjaWVzIGluIHJuYV9taXhlcyAlPiUKICAgIGZpbHRlcihncmVwbCgiUlVOWDEiLCBybmFfc3BlY2llcykpICU+JQogICAgcHVsbChybmFfc3BlY2llcykpIHsKICAgIHBlZ3JuYV9wbG90cygKICAgICAgc2VxdWVuY2VfbmFtZSA9IHJuYV9zcGVjaWVzLAogICAgICBub3JtYWxpemF0aW9uX2ZhY3RvciA9IG5vcm1fZmFjdG9yLAogICAgICB5bGFiID0gc3ByaW50ZigiQ292ZXJhZ2UgKG5vcm1hbGl6ZWQgdG8gJXMpIiwgbm9ybV9mYWN0b3IpLAogICAgKQogIH0KfQpgYGAKCiMjIyBFTVgxCgpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CmZvciAobm9ybV9mYWN0b3IgaW4gYygiZXhvZ2Vub3VzX3JuYV9tYXBwZWRfcmVhZHMiLCAiZ3JjaDM4X21hcHBlZF9yZWFkcyIpKSB7CiAgZm9yIChybmFfc3BlY2llcyBpbiBybmFfbWl4ZXMgJT4lCiAgICBmaWx0ZXIoZ3JlcGwoIkVNWDEiLCBybmFfc3BlY2llcykpICU+JQogICAgcHVsbChybmFfc3BlY2llcykpIHsKICAgIHBlZ3JuYV9wbG90cygKICAgICAgc2VxdWVuY2VfbmFtZSA9IHJuYV9zcGVjaWVzLAogICAgICBub3JtYWxpemF0aW9uX2ZhY3RvciA9IG5vcm1fZmFjdG9yLAogICAgICB5bGFiID0gc3ByaW50ZigiQ292ZXJhZ2UgKG5vcm1hbGl6ZWQgdG8gJXMpIiwgbm9ybV9mYWN0b3IpLAogICAgKQogIH0KfQpgYGAKCiMjIyBSTkYyCgpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CmZvciAobm9ybV9mYWN0b3IgaW4gYygiZXhvZ2Vub3VzX3JuYV9tYXBwZWRfcmVhZHMiLCAiZ3JjaDM4X21hcHBlZF9yZWFkcyIpKSB7CiAgZm9yIChybmFfc3BlY2llcyBpbiBybmFfbWl4ZXMgJT4lCiAgICBmaWx0ZXIoZ3JlcGwoIlJORjIiLCBybmFfc3BlY2llcykpICU+JQogICAgcHVsbChybmFfc3BlY2llcykpIHsKICAgIHBlZ3JuYV9wbG90cygKICAgICAgc2VxdWVuY2VfbmFtZSA9IHJuYV9zcGVjaWVzLAogICAgICBub3JtYWxpemF0aW9uX2ZhY3RvciA9IG5vcm1fZmFjdG9yLAogICAgICB5bGFiID0gc3ByaW50ZigiQ292ZXJhZ2UgKG5vcm1hbGl6ZWQgdG8gJXMpIiwgbm9ybV9mYWN0b3IpLAogICAgKQogIH0KfQpgYGAK